Previous script: “06_get_eigengene_QTL.Rmd”

The goal is to find QTL peaks for the WGCNA eigen genes and see if those overalp with any growth QTL. We are only focusing on eigen genes that correlated with some growth traits/paramters.

library(GenomicRanges)
Loading required package: stats4
Loading required package: BiocGenerics
Loading required package: parallel

Attaching package: ‘BiocGenerics’

The following objects are masked from ‘package:parallel’:

    clusterApply, clusterApplyLB, clusterCall, clusterEvalQ, clusterExport, clusterMap,
    parApply, parCapply, parLapply, parLapplyLB, parRapply, parSapply, parSapplyLB

The following objects are masked from ‘package:stats’:

    IQR, mad, sd, var, xtabs

The following objects are masked from ‘package:base’:

    anyDuplicated, append, as.data.frame, basename, cbind, colMeans, colnames, colSums,
    dirname, do.call, duplicated, eval, evalq, Filter, Find, get, grep, grepl, intersect,
    is.unsorted, lapply, lengths, Map, mapply, match, mget, order, paste, pmax, pmax.int,
    pmin, pmin.int, Position, rank, rbind, Reduce, rowMeans, rownames, rowSums, sapply,
    setdiff, sort, table, tapply, union, unique, unsplit, which, which.max, which.min

Loading required package: S4Vectors

Attaching package: ‘S4Vectors’

The following object is masked from ‘package:base’:

    expand.grid

Loading required package: IRanges
Loading required package: GenomeInfoDb
library(qtl)
library(tidyverse)
── Attaching packages ───────────────────────────────────────────────────────────────── tidyverse 1.2.1 ──
✔ ggplot2 3.0.0     ✔ purrr   0.2.5
✔ tibble  1.4.2     ✔ dplyr   0.7.6
✔ tidyr   0.8.1     ✔ stringr 1.3.1
✔ readr   1.1.1     ✔ forcats 0.3.0
── Conflicts ──────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::collapse()   masks IRanges::collapse()
✖ dplyr::combine()    masks BiocGenerics::combine()
✖ dplyr::desc()       masks IRanges::desc()
✖ tidyr::expand()     masks S4Vectors::expand()
✖ dplyr::filter()     masks stats::filter()
✖ dplyr::first()      masks S4Vectors::first()
✖ dplyr::lag()        masks stats::lag()
✖ ggplot2::Position() masks BiocGenerics::Position(), base::Position()
✖ purrr::reduce()     masks GenomicRanges::reduce(), IRanges::reduce()
✖ dplyr::rename()     masks S4Vectors::rename()
✖ dplyr::slice()      masks IRanges::slice()
library(stringr)
load("../output/scanone-eigengene-qtl_2012.RData")

scanone imp

Plot QTL

threshold.95 <- tibble(perm.threshold=lod.thrs[5,],
                       trait=colnames(lod.thrs))
scanone.gather <- scanone_eigen %>%
  gather(key = trait, value = LOD, -chr, -pos) %>%
  mutate(condition=str_sub(trait,1,2), color=str_sub(trait,6,100)) %>%
  left_join(threshold.95)
Joining, by = "trait"
scanone.gather
   pl.UN <- scanone.gather %>% filter(condition=="UN") %>%
  ggplot(aes(x=pos,y=LOD)) +
  geom_line() +
  geom_hline(aes(yintercept=perm.threshold),lty=2,lwd=.5,alpha=.5) +
  facet_grid(trait ~ chr, scales="free") +
  theme(strip.text.y = element_text(angle=0), axis.text.x = element_text(angle=90)) +
  ggtitle("UN Eigen Gene QTL")
pl.UN
ggsave("../output/eigen gene eQTL UN 2012.pdf",width=12,height=8)

Look for overlap

For each eigen gene, find QTL borders and look for overlap with growth QTL

For each eigen gene first identify chromosomes with “significant” peaks (in this case > 99% permuation threshold) and then run bayesint() on them to define the intervals

sig.chrs <- scanone.gather %>% filter(LOD > perm.threshold) %>%
  group_by(trait,chr) %>%
  summarize(unique(chr))
sig.chrs

now for each significant chromosome/trait combo run bayesint

scanone_eigen.phys <- scanone_eigen[!str_detect(rownames(scanone_eigen),"^cA"),]
bayesint.list <- apply(sig.chrs,1,function(hit) {
    result <- bayesint(scanone_eigen.phys[c("chr","pos",hit["trait"])], 
                     chr=hit["chr"], 
                     lodcolumn = 1,
                     expandtomarkers = TRUE
  )
  colnames(result)[3] <- "LOD"
  result
})
names(bayesint.list) <- sig.chrs$trait
bayesint.list <- lapply(bayesint.list,function(x) x %>% 
                          as.data.frame() %>%
                          rownames_to_column(var="markername")  %>%
                          mutate(chr=as.character(chr))
)
bayesint.result <- as.tibble(bind_rows(bayesint.list,.id="trait")) %>% 
  select(trait,chr,pos,markername,LOD) %>%
  separate(markername,into=c("chr1","Mbp"),sep="x", convert=TRUE) %>%
  group_by(trait,chr) %>% 
  summarize(start=min(Mbp),end=max(Mbp),min_eQTL_LOD=min(LOD),max_eQTL_LOD=max(LOD)) %>% 
  #for the high QTL peaks the interval width is 0.  That is overly precise and need to widen those.
  mutate(start=ifelse(start==end,max(0,start-20000),start), end=ifelse(start==end,end+20000,end))
  
  
bayesint.result

annotate Eigen gene QTL

Load annotation

BrapaAnnotation <- read_csv("../input/Brapa_V1.5_annotated.csv")
Missing column names filled in: 'X1' [1]Parsed with column specification:
cols(
  X1 = col_integer(),
  name = col_character(),
  chrom = col_character(),
  start = col_integer(),
  end = col_integer(),
  subject = col_character(),
  AGI = col_character(),
  At_symbol = col_character(),
  At_description = col_character(),
  perc_ID = col_double(),
  aln_length = col_integer(),
  mismatch = col_integer(),
  gap_open = col_integer(),
  qstart = col_integer(),
  qend = col_integer(),
  sstart = col_integer(),
  send = col_integer(),
  eval = col_double(),
  score = col_double()
)
BrapaAnnotation
eigen.annotated <- lapply(1:nrow(bayesint.result),function(row) {
  qtl <- bayesint.result[row,]
  results <- subset(BrapaAnnotation, chrom==qtl$chr &
                    start >= qtl$start &
                    end <= qtl$end)
}
)
names(eigen.annotated) <- bayesint.result$trait
eigen.annotated <- bind_rows(eigen.annotated,.id="trait") %>%
  mutate(chrom=as.character(chrom)) %>%
  left_join(bayesint.result,by=c("trait","chrom"="chr")) %>% #get eQTL LOD
  rename(eigen_eQTL_candidate=name)
eigen.annotated.small <- eigen.annotated %>% select(trait,eigen_eQTL_candidate,ends_with("LOD"))
eigen.annotated.small

given bayesint results, find overlaps with UN growth QTL

filepath <- "../input/All2012HeightQTL2.xlsx"
filebase <- filepath %>% basename() %>% str_replace("\\..*$","")
QTLgenes <- readxl::read_excel(filepath)[,-1]
QTLgenes <- QTLgenes %>% dplyr::rename(.id=QTL, FVTtrait=FVT) # change names to match previous file
QTLgenes <- QTLgenes %>% filter(str_detect(FVTtrait,"^UN"))
QTLgenes
eigen.qtl.combined <- inner_join(eigen.annotated.small,QTLgenes,by=c("eigen_eQTL_candidate"="name")) %>%
  select(.id, trait, everything())
eigen.qtl.combined

how many QTL have at least some overlap?

sort(unique(QTLgenes$.id))
 [1] "QTL1"  "QTL12" "QTL13" "QTL14" "QTL15" "QTL16" "QTL17" "QTL18" "QTL19" "QTL2"  "QTL3"  "QTL33"
[13] "QTL34" "QTL35" "QTL6"  "QTL7" 
sort(unique(eigen.qtl.combined$.id))
[1] "QTL1"  "QTL13" "QTL14" "QTL19" "QTL3"  "QTL35" "QTL6"  "QTL7" 

three of four

are all eigen genes overlapping?

unique(eigen.annotated.small$trait)
 [1] "UN_MEblue"          "UN_MEbrown"         "UN_MEcyan"          "UN_MEdarkslateblue"
 [5] "UN_MElightgreen"    "UN_MEmidnightblue"  "UN_MEpurple"        "UN_MEsteelblue"    
 [9] "UN_MEturquoise"     "UN_MEyellow"        "UN_MEyellowgreen"  
unique(eigen.qtl.combined$trait)
[1] "UN_MEblue"          "UN_MEbrown"         "UN_MEcyan"          "UN_MEdarkslateblue"
[5] "UN_MEmidnightblue"  "UN_MEpurple"        "UN_MEturquoise"     "UN_MEyellow"       
[9] "UN_MEyellowgreen"  

No, 7 of 11

write_csv(eigen.qtl.combined,
          path=str_c("../output/", filebase, "_eigenQTL_overlap_", Sys.Date(), ".csv"))

overlaps and significance

first convert things to ranges

qtl.info <- QTLgenes %>%
  group_by(.id) %>%
  summarize(chrom=unique(chrom),start=min(start),end=max(end))
qtl.info
qtl.ranges <- GRanges(seqnames = qtl.info$chrom,ranges=IRanges(start=qtl.info$start,end=qtl.info$end))
qtl.ranges <- GenomicRanges::reduce(qtl.ranges)
eQTL.ranges <- GRanges(bayesint.result$chr,
                       ranges = IRanges(start=bayesint.result$start,
                                        end=bayesint.result$end))
eQTL.ranges <- GenomicRanges::reduce(eQTL.ranges)

Make table of chromosome info

chr.info <- scanone_eigen.phys %>% 
  as.data.frame() %>%
  rownames_to_column("marker") %>%
  select(marker) %>%
  separate(marker,into=c("chr","bp"),sep="x",convert=TRUE) %>%
  group_by(chr) %>%
  summarize(start=min(bp),end=max(bp))

Do the simulations

sims <- 1000
set.seed(2323)
sim.results <- sapply(1:sims, function(s) {
  if (s %% 100 == 0) print(s)
  sim.eQTL <- tibble(
    chr=sample(chr.info$chr,
               size = length(eQTL.ranges),
               replace = TRUE,
               prob=chr.info$end/sum(chr.info$end)),
    width=width(eQTL.ranges) # width of the QTL to simulate
  )
  sim.eQTL <- chr.info %>% 
    select(chr,chr.start=start,chr.end=end) %>% right_join(sim.eQTL,by="chr") #need to get the chrom end so we can sample correctly
  sim.eQTL <- sim.eQTL %>% mutate(qtl.start = runif(n=n(),
                                                    min = chr.start,
                                                    max= max(chr.start,chr.end-width)),
                                  qtl.end=qtl.start+width)
  sim.eQTL.ranges <- GRanges(seqnames = sim.eQTL$chr,
                             ranges = IRanges(start=sim.eQTL$qtl.start,
                                              end=sim.eQTL$qtl.end))
  
  suppressWarnings(result <- sum(countOverlaps(qtl.ranges,sim.eQTL.ranges)>0))
  result
})
[1] 100
[1] 200
[1] 300
[1] 400
[1] 500
[1] 600
[1] 700
[1] 800
[1] 900
[1] 1000
true.overlap <- sum(countOverlaps(qtl.ranges,eQTL.ranges)) #OK to ignore warnings
Each of the 2 combined objects has sequence levels not in the other:
  - in 'x': A09, A05
  - in 'y': A08
  Make sure to always combine/compare objects based on the same reference
  genome (use suppressWarnings() to suppress this warning).
true.overlap
[1] 4
mean(sim.results >= true.overlap)
[1] 0.065
tibble(FVTQTL_vs_MReQTL_True_Overlaps=true.overlap,
       N_Simulations_fewer_overlaps=sum(sim.results < true.overlap),
       N_Simulations_greater_equal_overlaps=sum(sim.results >= true.overlap),
       P_value=mean(sim.results >= true.overlap)
) %>%
  write_csv(str_c("../output/", filebase, "_WGCNA_eigen_eQTL_scanone_overlap_pval_", Sys.Date(), ".csv"))

cim

Plot QTL

threshold.95 <- tibble(perm.threshold=lod.thrs.cim[5,],
                       trait=colnames(lod.thrs.cim))
scanone.gather <- scanone_eigen_cim %>%
  gather(key = trait, value = LOD, -chr, -pos) %>%
  mutate(condition=str_sub(trait,1,2), color=str_sub(trait,6,100)) %>%
  left_join(threshold.95)
Joining, by = "trait"
scanone.gather
   pl.UN <- scanone.gather %>% filter(condition=="UN") %>%
  ggplot(aes(x=pos,y=LOD)) +
  geom_line() +
  geom_hline(aes(yintercept=perm.threshold),lty=2,lwd=.5,alpha=.5) +
  facet_grid(trait ~ chr, scales="free") +
  theme(strip.text.y = element_text(angle=0), axis.text.x = element_text(angle=90)) +
  ggtitle("UN Eigen Gene QTL")
pl.UN
ggsave("../output/eigen gene eQTL UN CIM 2012.pdf",width=12,height=8)

Look for overlap

For each eigen gene, find QTL borders and look for overlap with growth QTL

For each eigen gene first identify chromosomes with “significant” peaks (in this case > 99% permuation threshold) and then runs bayesint() on them to define the intervals

sig.chrs <- scanone.gather %>% filter(LOD > perm.threshold) %>%
  group_by(trait,chr) %>%
  summarize(unique(chr))
sig.chrs

now for each significant chromosome/trait combo run bayesint

#remove markers without physical position
scanone_eigen_cim.phys <- scanone_eigen_cim[!str_detect(rownames(scanone_eigen),"^cA"),]
bayesint.list <- apply(sig.chrs,1,function(hit) {
    result <- bayesint(scanone_eigen_cim.phys[c("chr","pos",hit["trait"])], 
                     chr=hit["chr"], 
                     lodcolumn = 1,
                     expandtomarkers = TRUE
  )
  colnames(result)[3] <- "LOD"
  result
})
names(bayesint.list) <- sig.chrs$trait
bayesint.list <- lapply(bayesint.list,function(x) x %>% 
                          as.data.frame() %>%
                          rownames_to_column(var="markername")  %>%
                          mutate(chr=as.character(chr))
)
bayesint.result <- as.tibble(bind_rows(bayesint.list,.id="trait")) %>% 
  select(trait,chr,pos,markername,LOD) %>%
  separate(markername,into=c("chr1","Mbp"),sep="x", convert=TRUE) %>%
  group_by(trait,chr) %>% 
  summarize(start=min(Mbp),end=max(Mbp),min_eQTL_LOD=min(LOD),max_eQTL_LOD=max(LOD)) %>% 
  #for the high QTL peaks the interval width is 0.  That is overly precise and need to widen those.
  mutate(start=ifelse(start==end,max(0,start-20000),start), end=ifelse(start==end,end+20000,end))
  
  
bayesint.result

annotate Eigen gene QTL

Load annotation

BrapaAnnotation <- read_csv("../input/Brapa_V1.5_annotated.csv")
Missing column names filled in: 'X1' [1]Parsed with column specification:
cols(
  X1 = col_integer(),
  name = col_character(),
  chrom = col_character(),
  start = col_integer(),
  end = col_integer(),
  subject = col_character(),
  AGI = col_character(),
  At_symbol = col_character(),
  At_description = col_character(),
  perc_ID = col_double(),
  aln_length = col_integer(),
  mismatch = col_integer(),
  gap_open = col_integer(),
  qstart = col_integer(),
  qend = col_integer(),
  sstart = col_integer(),
  send = col_integer(),
  eval = col_double(),
  score = col_double()
)

|============                                                                                      |  13%
|=============                                                                             |  14%    1 MB
|==============                                                                            |  15%    1 MB
|===============                                                                           |  16%    1 MB
|================                                                                          |  17%    1 MB
|=================                                                                         |  19%    1 MB
|==================                                                                        |  20%    1 MB
|===================                                                                       |  21%    1 MB
|====================                                                                      |  22%    1 MB
|=====================                                                                     |  24%    1 MB
|=======================                                                                   |  25%    1 MB
|========================                                                                  |  26%    1 MB
|=========================                                                                 |  27%    2 MB
|==========================                                                                |  29%    2 MB
|===========================                                                               |  30%    2 MB
|============================                                                              |  31%    2 MB
|=============================                                                             |  32%    2 MB
|===============================                                                           |  34%    2 MB
|================================                                                          |  35%    2 MB
|=================================                                                         |  36%    2 MB
|==================================                                                        |  37%    2 MB
|===================================                                                       |  38%    2 MB
|====================================                                                      |  40%    2 MB
|=====================================                                                     |  41%    3 MB
|======================================                                                    |  42%    3 MB
|=======================================                                                   |  43%    3 MB
|========================================                                                  |  44%    3 MB
|==========================================                                                |  46%    3 MB
|===========================================                                               |  47%    3 MB
|============================================                                              |  48%    3 MB
|=============================================                                             |  49%    3 MB
|==============================================                                            |  51%    3 MB
|===============================================                                           |  52%    3 MB
|================================================                                          |  53%    3 MB
|=================================================                                         |  54%    4 MB
|==================================================                                        |  55%    4 MB
|===================================================                                       |  57%    4 MB
|=====================================================                                     |  58%    4 MB
|======================================================                                    |  59%    4 MB
|=======================================================                                   |  60%    4 MB
|========================================================                                  |  61%    4 MB
|=========================================================                                 |  62%    4 MB
|==========================================================                                |  64%    4 MB
|===========================================================                               |  65%    4 MB
|============================================================                              |  66%    4 MB
|=============================================================                             |  67%    5 MB
|==============================================================                            |  69%    5 MB
|===============================================================                           |  70%    5 MB
|================================================================                          |  71%    5 MB
|==================================================================                        |  72%    5 MB
|===================================================================                       |  73%    5 MB
|====================================================================                      |  75%    5 MB
|=====================================================================                     |  76%    5 MB
|======================================================================                    |  77%    5 MB
|=======================================================================                   |  78%    5 MB
|========================================================================                  |  80%    5 MB
|=========================================================================                 |  81%    6 MB
|==========================================================================                |  82%    6 MB
|============================================================================              |  83%    6 MB
|=============================================================================             |  84%    6 MB
|==============================================================================            |  86%    6 MB
|===============================================================================           |  87%    6 MB
|================================================================================          |  88%    6 MB
|=================================================================================         |  89%    6 MB
|==================================================================================        |  90%    6 MB
|===================================================================================       |  92%    6 MB
|=====================================================================================     |  93%    6 MB
|======================================================================================    |  94%    7 MB
|=======================================================================================   |  95%    7 MB
|========================================================================================  |  97%    7 MB
|========================================================================================= |  98%    7 MB
|==========================================================================================|  99%    7 MB
|===========================================================================================| 100%    7 MB
BrapaAnnotation
eigen.annotated <- lapply(1:nrow(bayesint.result),function(row) {
  qtl <- bayesint.result[row,]
  results <- subset(BrapaAnnotation, chrom==qtl$chr &
                    start >= qtl$start &
                    end <= qtl$end)
}
)
names(eigen.annotated) <- bayesint.result$trait
eigen.annotated <- bind_rows(eigen.annotated,.id="trait") %>%
  mutate(chrom=as.character(chrom)) %>%
  left_join(bayesint.result,by=c("trait","chrom"="chr")) %>% #get eQTL LOD
  rename(eigen_eQTL_candidate=name)
eigen.annotated.small <- eigen.annotated %>% select(trait,eigen_eQTL_candidate,ends_with("LOD"))
eigen.annotated.small

given bayesint results, find overlaps with UN growth QTL

filepath <- "../input/All2012HeightQTL2.xlsx"
filebase <- filepath %>% basename() %>% str_replace("\\..*$","")
QTLgenes <- readxl::read_excel(filepath)[,-1]
QTLgenes <- QTLgenes %>% dplyr::rename(.id=QTL, FVTtrait=FVT) # change names to match previous file
QTLgenes <- QTLgenes %>% filter(str_detect(FVTtrait,"^UN"))
QTLgenes
eigen.qtl.combined <- inner_join(eigen.annotated.small,QTLgenes,by=c("eigen_eQTL_candidate"="name")) %>%
  select(.id, trait, everything())
eigen.qtl.combined

how many QTL have at least some overlap?

unique(QTLgenes$.id)
 [1] "QTL1"  "QTL12" "QTL13" "QTL14" "QTL15" "QTL16" "QTL17" "QTL18" "QTL19" "QTL2"  "QTL3"  "QTL33"
[13] "QTL34" "QTL35" "QTL6"  "QTL7" 
unique(eigen.qtl.combined$.id)
[1] "QTL3"  "QTL7"  "QTL19" "QTL1"  "QTL13" "QTL6" 

two of four

are all eigen genes overlapping?

unique(eigen.annotated.small$trait)
[1] "UN_MEblue"          "UN_MEbrown"         "UN_MEcyan"          "UN_MEdarkslateblue"
[5] "UN_MElightgreen"    "UN_MEmidnightblue"  "UN_MEsteelblue"     "UN_MEturquoise"    
[9] "UN_MEyellowgreen"  
unique(eigen.qtl.combined$trait)
[1] "UN_MEbrown"         "UN_MEcyan"          "UN_MEdarkslateblue" "UN_MEmidnightblue" 
[5] "UN_MEturquoise"     "UN_MEyellowgreen"  

No, 2

write_csv(eigen.qtl.combined,
          path=str_c("../output/", filebase, "_eigenQTL_overlap_CIM_", Sys.Date(), ".csv"))

overlaps and signficance

eQTL.ranges <- GRanges(bayesint.result$chr,
                       ranges = IRanges(start=bayesint.result$start,
                                        end=bayesint.result$end))
eQTL.ranges <- GenomicRanges::reduce(eQTL.ranges)

Do the simulations

sims <- 1000
set.seed(4545)
sim.results <- sapply(1:sims, function(s) {
  if (s %% 100 == 0) print(s)
  sim.eQTL <- tibble(
    chr=sample(chr.info$chr,
               size = length(eQTL.ranges),
               replace = TRUE,
               prob=chr.info$end/sum(chr.info$end)),
    width=width(eQTL.ranges) # width of the QTL to simulate
  )
  sim.eQTL <- chr.info %>% 
    select(chr,chr.start=start,chr.end=end) %>% right_join(sim.eQTL,by="chr") #need to get the chrom end so we can sample correctly
  sim.eQTL <- sim.eQTL %>% mutate(qtl.start = runif(n=n(),
                                                    min = chr.start,
                                                    max= max(chr.start,chr.end-width)),
                                  qtl.end=qtl.start+width)
  sim.eQTL.ranges <- GRanges(seqnames = sim.eQTL$chr,
                             ranges = IRanges(start=sim.eQTL$qtl.start,
                                              end=sim.eQTL$qtl.end))
  
  suppressWarnings(result <- sum(countOverlaps(qtl.ranges,sim.eQTL.ranges)>0))
  result
})
[1] 100
[1] 200
[1] 300
[1] 400
[1] 500
[1] 600
[1] 700
[1] 800
[1] 900
[1] 1000
true.overlap <- sum(countOverlaps(qtl.ranges,eQTL.ranges)) #OK to ignore warnings
true.overlap
[1] 4
mean(sim.results >= true.overlap)
[1] 0.005
tibble(FVTQTL_vs_MReQTL_True_Overlaps=true.overlap,
       N_Simulations_fewer_overlaps=sum(sim.results < true.overlap),
       N_Simulations_greater_equal_overlaps=sum(sim.results >= true.overlap),
       P_value=mean(sim.results >= true.overlap)
) %>%
  write_csv(str_c("../output/", filebase, "_WGCNA_eigen_eQTL_CIM_overlap_pval_", Sys.Date(), ".csv"))
LS0tCnRpdGxlOiAiQW5hbHl6ZSBFaWdlbiBHZW5lIFFUTCIKb3V0cHV0OiBodG1sX25vdGVib29rCmF1dGhvcjogIkp1bGluIE1hbG9vZiIKLS0tCgoKUHJldmlvdXMgc2NyaXB0OiAiMDZfZ2V0X2VpZ2VuZ2VuZV9RVEwuUm1kIgoKVGhlIGdvYWwgaXMgdG8gZmluZCBRVEwgcGVha3MgZm9yIHRoZSBXR0NOQSBlaWdlbiBnZW5lcyBhbmQgc2VlIGlmIHRob3NlIG92ZXJhbHAgd2l0aCBhbnkgZ3Jvd3RoIFFUTC4gIFdlIGFyZSBvbmx5IGZvY3VzaW5nIG9uIGVpZ2VuIGdlbmVzIHRoYXQgY29ycmVsYXRlZCB3aXRoIHNvbWUgZ3Jvd3RoIHRyYWl0cy9wYXJhbXRlcnMuCgpgYGB7cn0KbGlicmFyeShHZW5vbWljUmFuZ2VzKQpsaWJyYXJ5KHF0bCkKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoc3RyaW5ncikKbG9hZCgiLi4vb3V0cHV0L3NjYW5vbmUtZWlnZW5nZW5lLXF0bF8yMDEyLlJEYXRhIikKYGBgCgojIHNjYW5vbmUgaW1wCgojIyBQbG90IFFUTAoKYGBge3J9Cgp0aHJlc2hvbGQuOTUgPC0gdGliYmxlKHBlcm0udGhyZXNob2xkPWxvZC50aHJzWzUsXSwKICAgICAgICAgICAgICAgICAgICAgICB0cmFpdD1jb2xuYW1lcyhsb2QudGhycykpCgpzY2Fub25lLmdhdGhlciA8LSBzY2Fub25lX2VpZ2VuICU+JQogIGdhdGhlcihrZXkgPSB0cmFpdCwgdmFsdWUgPSBMT0QsIC1jaHIsIC1wb3MpICU+JQogIG11dGF0ZShjb25kaXRpb249c3RyX3N1Yih0cmFpdCwxLDIpLCBjb2xvcj1zdHJfc3ViKHRyYWl0LDYsMTAwKSkgJT4lCiAgbGVmdF9qb2luKHRocmVzaG9sZC45NSkKCnNjYW5vbmUuZ2F0aGVyCmBgYAoKYGBge3J9CiAgIHBsLlVOIDwtIHNjYW5vbmUuZ2F0aGVyICU+JSBmaWx0ZXIoY29uZGl0aW9uPT0iVU4iKSAlPiUKICBnZ3Bsb3QoYWVzKHg9cG9zLHk9TE9EKSkgKwogIGdlb21fbGluZSgpICsKICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0PXBlcm0udGhyZXNob2xkKSxsdHk9Mixsd2Q9LjUsYWxwaGE9LjUpICsKICBmYWNldF9ncmlkKHRyYWl0IH4gY2hyLCBzY2FsZXM9ImZyZWUiKSArCiAgdGhlbWUoc3RyaXAudGV4dC55ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTApLCBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT05MCkpICsKICBnZ3RpdGxlKCJVTiBFaWdlbiBHZW5lIFFUTCIpCnBsLlVOCmdnc2F2ZSgiLi4vb3V0cHV0L2VpZ2VuIGdlbmUgZVFUTCBVTiAyMDEyLnBkZiIsd2lkdGg9MTIsaGVpZ2h0PTgpCmBgYAoKCiMjIExvb2sgZm9yIG92ZXJsYXAKCkZvciBlYWNoIGVpZ2VuIGdlbmUsIGZpbmQgUVRMIGJvcmRlcnMgYW5kIGxvb2sgZm9yIG92ZXJsYXAgd2l0aCBncm93dGggUVRMCgpGb3IgZWFjaCBlaWdlbiBnZW5lIGZpcnN0IGlkZW50aWZ5IGNocm9tb3NvbWVzIHdpdGggInNpZ25pZmljYW50IiBwZWFrcyAoaW4gdGhpcyBjYXNlID4gOTklIHBlcm11YXRpb24gdGhyZXNob2xkKSBhbmQgdGhlbiBydW4gYmF5ZXNpbnQoKSBvbiB0aGVtIHRvIGRlZmluZSB0aGUgaW50ZXJ2YWxzCgpgYGB7cn0Kc2lnLmNocnMgPC0gc2Nhbm9uZS5nYXRoZXIgJT4lIGZpbHRlcihMT0QgPiBwZXJtLnRocmVzaG9sZCkgJT4lCiAgZ3JvdXBfYnkodHJhaXQsY2hyKSAlPiUKICBzdW1tYXJpemUodW5pcXVlKGNocikpCnNpZy5jaHJzCmBgYAoKbm93IGZvciBlYWNoIHNpZ25pZmljYW50IGNocm9tb3NvbWUvdHJhaXQgY29tYm8gcnVuIGJheWVzaW50CgpgYGB7cn0Kc2Nhbm9uZV9laWdlbi5waHlzIDwtIHNjYW5vbmVfZWlnZW5bIXN0cl9kZXRlY3Qocm93bmFtZXMoc2Nhbm9uZV9laWdlbiksIl5jQSIpLF0KCmJheWVzaW50Lmxpc3QgPC0gYXBwbHkoc2lnLmNocnMsMSxmdW5jdGlvbihoaXQpIHsKICAgIHJlc3VsdCA8LSBiYXllc2ludChzY2Fub25lX2VpZ2VuLnBoeXNbYygiY2hyIiwicG9zIixoaXRbInRyYWl0Il0pXSwgCiAgICAgICAgICAgICAgICAgICAgIGNocj1oaXRbImNociJdLCAKICAgICAgICAgICAgICAgICAgICAgbG9kY29sdW1uID0gMSwKICAgICAgICAgICAgICAgICAgICAgZXhwYW5kdG9tYXJrZXJzID0gVFJVRQogICkKICBjb2xuYW1lcyhyZXN1bHQpWzNdIDwtICJMT0QiCiAgcmVzdWx0Cn0pCgpuYW1lcyhiYXllc2ludC5saXN0KSA8LSBzaWcuY2hycyR0cmFpdAoKYmF5ZXNpbnQubGlzdCA8LSBsYXBwbHkoYmF5ZXNpbnQubGlzdCxmdW5jdGlvbih4KSB4ICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgcm93bmFtZXNfdG9fY29sdW1uKHZhcj0ibWFya2VybmFtZSIpICAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICBtdXRhdGUoY2hyPWFzLmNoYXJhY3RlcihjaHIpKQopCgpiYXllc2ludC5yZXN1bHQgPC0gYXMudGliYmxlKGJpbmRfcm93cyhiYXllc2ludC5saXN0LC5pZD0idHJhaXQiKSkgJT4lIAogIHNlbGVjdCh0cmFpdCxjaHIscG9zLG1hcmtlcm5hbWUsTE9EKSAlPiUKICBzZXBhcmF0ZShtYXJrZXJuYW1lLGludG89YygiY2hyMSIsIk1icCIpLHNlcD0ieCIsIGNvbnZlcnQ9VFJVRSkgJT4lCiAgZ3JvdXBfYnkodHJhaXQsY2hyKSAlPiUgCiAgc3VtbWFyaXplKHN0YXJ0PW1pbihNYnApLGVuZD1tYXgoTWJwKSxtaW5fZVFUTF9MT0Q9bWluKExPRCksbWF4X2VRVExfTE9EPW1heChMT0QpKSAlPiUgCiAgI2ZvciB0aGUgaGlnaCBRVEwgcGVha3MgdGhlIGludGVydmFsIHdpZHRoIGlzIDAuICBUaGF0IGlzIG92ZXJseSBwcmVjaXNlIGFuZCBuZWVkIHRvIHdpZGVuIHRob3NlLgogIG11dGF0ZShzdGFydD1pZmVsc2Uoc3RhcnQ9PWVuZCxtYXgoMCxzdGFydC0yMDAwMCksc3RhcnQpLCBlbmQ9aWZlbHNlKHN0YXJ0PT1lbmQsZW5kKzIwMDAwLGVuZCkpCiAgCiAgCmJheWVzaW50LnJlc3VsdApgYGAKCiMjIyBhbm5vdGF0ZSBFaWdlbiBnZW5lIFFUTAoKTG9hZCBhbm5vdGF0aW9uCmBgYHtyfQpCcmFwYUFubm90YXRpb24gPC0gcmVhZF9jc3YoIi4uL2lucHV0L0JyYXBhX1YxLjVfYW5ub3RhdGVkLmNzdiIpCkJyYXBhQW5ub3RhdGlvbgpgYGAKCmBgYHtyfQplaWdlbi5hbm5vdGF0ZWQgPC0gbGFwcGx5KDE6bnJvdyhiYXllc2ludC5yZXN1bHQpLGZ1bmN0aW9uKHJvdykgewogIHF0bCA8LSBiYXllc2ludC5yZXN1bHRbcm93LF0KICByZXN1bHRzIDwtIHN1YnNldChCcmFwYUFubm90YXRpb24sIGNocm9tPT1xdGwkY2hyICYKICAgICAgICAgICAgICAgICAgICBzdGFydCA+PSBxdGwkc3RhcnQgJgogICAgICAgICAgICAgICAgICAgIGVuZCA8PSBxdGwkZW5kKQp9CikKbmFtZXMoZWlnZW4uYW5ub3RhdGVkKSA8LSBiYXllc2ludC5yZXN1bHQkdHJhaXQKCmVpZ2VuLmFubm90YXRlZCA8LSBiaW5kX3Jvd3MoZWlnZW4uYW5ub3RhdGVkLC5pZD0idHJhaXQiKSAlPiUKICBtdXRhdGUoY2hyb209YXMuY2hhcmFjdGVyKGNocm9tKSkgJT4lCiAgbGVmdF9qb2luKGJheWVzaW50LnJlc3VsdCxieT1jKCJ0cmFpdCIsImNocm9tIj0iY2hyIikpICU+JSAjZ2V0IGVRVEwgTE9ECiAgcmVuYW1lKGVpZ2VuX2VRVExfY2FuZGlkYXRlPW5hbWUpCgplaWdlbi5hbm5vdGF0ZWQuc21hbGwgPC0gZWlnZW4uYW5ub3RhdGVkICU+JSBzZWxlY3QodHJhaXQsZWlnZW5fZVFUTF9jYW5kaWRhdGUsZW5kc193aXRoKCJMT0QiKSkKCmVpZ2VuLmFubm90YXRlZC5zbWFsbApgYGAKCmdpdmVuIGJheWVzaW50IHJlc3VsdHMsIGZpbmQgb3ZlcmxhcHMgd2l0aCBVTiBncm93dGggUVRMCgpgYGB7cn0KZmlsZXBhdGggPC0gIi4uL2lucHV0L0FsbDIwMTJIZWlnaHRRVEwyLnhsc3giCmZpbGViYXNlIDwtIGZpbGVwYXRoICU+JSBiYXNlbmFtZSgpICU+JSBzdHJfcmVwbGFjZSgiXFwuLiokIiwiIikKClFUTGdlbmVzIDwtIHJlYWR4bDo6cmVhZF9leGNlbChmaWxlcGF0aClbLC0xXQpRVExnZW5lcyA8LSBRVExnZW5lcyAlPiUgZHBseXI6OnJlbmFtZSguaWQ9UVRMLCBGVlR0cmFpdD1GVlQpICMgY2hhbmdlIG5hbWVzIHRvIG1hdGNoIHByZXZpb3VzIGZpbGUKUVRMZ2VuZXMgPC0gUVRMZ2VuZXMgJT4lIGZpbHRlcihzdHJfZGV0ZWN0KEZWVHRyYWl0LCJeVU4iKSkKUVRMZ2VuZXMKYGBgCgpgYGB7cn0KZWlnZW4ucXRsLmNvbWJpbmVkIDwtIGlubmVyX2pvaW4oZWlnZW4uYW5ub3RhdGVkLnNtYWxsLFFUTGdlbmVzLGJ5PWMoImVpZ2VuX2VRVExfY2FuZGlkYXRlIj0ibmFtZSIpKSAlPiUKICBzZWxlY3QoLmlkLCB0cmFpdCwgZXZlcnl0aGluZygpKQplaWdlbi5xdGwuY29tYmluZWQKYGBgCgpob3cgbWFueSBRVEwgaGF2ZSBhdCBsZWFzdCBzb21lIG92ZXJsYXA/CmBgYHtyfQpzb3J0KHVuaXF1ZShRVExnZW5lcyQuaWQpKQpzb3J0KHVuaXF1ZShlaWdlbi5xdGwuY29tYmluZWQkLmlkKSkKYGBgCgp0aHJlZSBvZiBmb3VyCgphcmUgYWxsIGVpZ2VuIGdlbmVzIG92ZXJsYXBwaW5nPwoKYGBge3J9CnVuaXF1ZShlaWdlbi5hbm5vdGF0ZWQuc21hbGwkdHJhaXQpCnVuaXF1ZShlaWdlbi5xdGwuY29tYmluZWQkdHJhaXQpCmBgYAoKTm8sIDcgb2YgMTEKCmBgYHtyfQp3cml0ZV9jc3YoZWlnZW4ucXRsLmNvbWJpbmVkLAogICAgICAgICAgcGF0aD1zdHJfYygiLi4vb3V0cHV0LyIsIGZpbGViYXNlLCAiX2VpZ2VuUVRMX292ZXJsYXBfIiwgU3lzLkRhdGUoKSwgIi5jc3YiKSkKYGBgCgojIyBvdmVybGFwcyBhbmQgc2lnbmlmaWNhbmNlCgpmaXJzdCBjb252ZXJ0IHRoaW5ncyB0byByYW5nZXMKYGBge3J9CnF0bC5pbmZvIDwtIFFUTGdlbmVzICU+JQogIGdyb3VwX2J5KC5pZCkgJT4lCiAgc3VtbWFyaXplKGNocm9tPXVuaXF1ZShjaHJvbSksc3RhcnQ9bWluKHN0YXJ0KSxlbmQ9bWF4KGVuZCkpCnF0bC5pbmZvCnF0bC5yYW5nZXMgPC0gR1JhbmdlcyhzZXFuYW1lcyA9IHF0bC5pbmZvJGNocm9tLHJhbmdlcz1JUmFuZ2VzKHN0YXJ0PXF0bC5pbmZvJHN0YXJ0LGVuZD1xdGwuaW5mbyRlbmQpKQpxdGwucmFuZ2VzIDwtIEdlbm9taWNSYW5nZXM6OnJlZHVjZShxdGwucmFuZ2VzKQpgYGAKCmBgYHtyfQplUVRMLnJhbmdlcyA8LSBHUmFuZ2VzKGJheWVzaW50LnJlc3VsdCRjaHIsCiAgICAgICAgICAgICAgICAgICAgICAgcmFuZ2VzID0gSVJhbmdlcyhzdGFydD1iYXllc2ludC5yZXN1bHQkc3RhcnQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlbmQ9YmF5ZXNpbnQucmVzdWx0JGVuZCkpCmVRVEwucmFuZ2VzIDwtIEdlbm9taWNSYW5nZXM6OnJlZHVjZShlUVRMLnJhbmdlcykKYGBgCgpNYWtlIHRhYmxlIG9mIGNocm9tb3NvbWUgaW5mbwpgYGB7cn0KY2hyLmluZm8gPC0gc2Nhbm9uZV9laWdlbi5waHlzICU+JSAKICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgcm93bmFtZXNfdG9fY29sdW1uKCJtYXJrZXIiKSAlPiUKICBzZWxlY3QobWFya2VyKSAlPiUKICBzZXBhcmF0ZShtYXJrZXIsaW50bz1jKCJjaHIiLCJicCIpLHNlcD0ieCIsY29udmVydD1UUlVFKSAlPiUKICBncm91cF9ieShjaHIpICU+JQogIHN1bW1hcml6ZShzdGFydD1taW4oYnApLGVuZD1tYXgoYnApKQpgYGAKCkRvIHRoZSBzaW11bGF0aW9ucwpgYGB7cn0Kc2ltcyA8LSAxMDAwCgpzZXQuc2VlZCgyMzIzKQpzaW0ucmVzdWx0cyA8LSBzYXBwbHkoMTpzaW1zLCBmdW5jdGlvbihzKSB7CiAgaWYgKHMgJSUgMTAwID09IDApIHByaW50KHMpCiAgc2ltLmVRVEwgPC0gdGliYmxlKAogICAgY2hyPXNhbXBsZShjaHIuaW5mbyRjaHIsCiAgICAgICAgICAgICAgIHNpemUgPSBsZW5ndGgoZVFUTC5yYW5nZXMpLAogICAgICAgICAgICAgICByZXBsYWNlID0gVFJVRSwKICAgICAgICAgICAgICAgcHJvYj1jaHIuaW5mbyRlbmQvc3VtKGNoci5pbmZvJGVuZCkpLAogICAgd2lkdGg9d2lkdGgoZVFUTC5yYW5nZXMpICMgd2lkdGggb2YgdGhlIFFUTCB0byBzaW11bGF0ZQogICkKICBzaW0uZVFUTCA8LSBjaHIuaW5mbyAlPiUgCiAgICBzZWxlY3QoY2hyLGNoci5zdGFydD1zdGFydCxjaHIuZW5kPWVuZCkgJT4lIHJpZ2h0X2pvaW4oc2ltLmVRVEwsYnk9ImNociIpICNuZWVkIHRvIGdldCB0aGUgY2hyb20gZW5kIHNvIHdlIGNhbiBzYW1wbGUgY29ycmVjdGx5CiAgc2ltLmVRVEwgPC0gc2ltLmVRVEwgJT4lIG11dGF0ZShxdGwuc3RhcnQgPSBydW5pZihuPW4oKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbiA9IGNoci5zdGFydCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heD0gbWF4KGNoci5zdGFydCxjaHIuZW5kLXdpZHRoKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdGwuZW5kPXF0bC5zdGFydCt3aWR0aCkKICBzaW0uZVFUTC5yYW5nZXMgPC0gR1JhbmdlcyhzZXFuYW1lcyA9IHNpbS5lUVRMJGNociwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICByYW5nZXMgPSBJUmFuZ2VzKHN0YXJ0PXNpbS5lUVRMJHF0bC5zdGFydCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVuZD1zaW0uZVFUTCRxdGwuZW5kKSkKICAKICBzdXBwcmVzc1dhcm5pbmdzKHJlc3VsdCA8LSBzdW0oY291bnRPdmVybGFwcyhxdGwucmFuZ2VzLHNpbS5lUVRMLnJhbmdlcyk+MCkpCiAgcmVzdWx0Cn0pCgpgYGAKCgpgYGB7cn0KdHJ1ZS5vdmVybGFwIDwtIHN1bShjb3VudE92ZXJsYXBzKHF0bC5yYW5nZXMsZVFUTC5yYW5nZXMpKSAjT0sgdG8gaWdub3JlIHdhcm5pbmdzCgp0cnVlLm92ZXJsYXAKCm1lYW4oc2ltLnJlc3VsdHMgPj0gdHJ1ZS5vdmVybGFwKQoKdGliYmxlKEZWVFFUTF92c19NUmVRVExfVHJ1ZV9PdmVybGFwcz10cnVlLm92ZXJsYXAsCiAgICAgICBOX1NpbXVsYXRpb25zX2Zld2VyX292ZXJsYXBzPXN1bShzaW0ucmVzdWx0cyA8IHRydWUub3ZlcmxhcCksCiAgICAgICBOX1NpbXVsYXRpb25zX2dyZWF0ZXJfZXF1YWxfb3ZlcmxhcHM9c3VtKHNpbS5yZXN1bHRzID49IHRydWUub3ZlcmxhcCksCiAgICAgICBQX3ZhbHVlPW1lYW4oc2ltLnJlc3VsdHMgPj0gdHJ1ZS5vdmVybGFwKQopICU+JQogIHdyaXRlX2NzdihzdHJfYygiLi4vb3V0cHV0LyIsIGZpbGViYXNlLCAiX1dHQ05BX2VpZ2VuX2VRVExfc2Nhbm9uZV9vdmVybGFwX3B2YWxfIiwgU3lzLkRhdGUoKSwgIi5jc3YiKSkKYGBgCgojIGNpbQoKIyMgUGxvdCBRVEwKCmBgYHtyfQoKdGhyZXNob2xkLjk1IDwtIHRpYmJsZShwZXJtLnRocmVzaG9sZD1sb2QudGhycy5jaW1bNSxdLAogICAgICAgICAgICAgICAgICAgICAgIHRyYWl0PWNvbG5hbWVzKGxvZC50aHJzLmNpbSkpCgpzY2Fub25lLmdhdGhlciA8LSBzY2Fub25lX2VpZ2VuX2NpbSAlPiUKICBnYXRoZXIoa2V5ID0gdHJhaXQsIHZhbHVlID0gTE9ELCAtY2hyLCAtcG9zKSAlPiUKICBtdXRhdGUoY29uZGl0aW9uPXN0cl9zdWIodHJhaXQsMSwyKSwgY29sb3I9c3RyX3N1Yih0cmFpdCw2LDEwMCkpICU+JQogIGxlZnRfam9pbih0aHJlc2hvbGQuOTUpCgpzY2Fub25lLmdhdGhlcgpgYGAKCmBgYHtyfQogICBwbC5VTiA8LSBzY2Fub25lLmdhdGhlciAlPiUgZmlsdGVyKGNvbmRpdGlvbj09IlVOIikgJT4lCiAgZ2dwbG90KGFlcyh4PXBvcyx5PUxPRCkpICsKICBnZW9tX2xpbmUoKSArCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdD1wZXJtLnRocmVzaG9sZCksbHR5PTIsbHdkPS41LGFscGhhPS41KSArCiAgZmFjZXRfZ3JpZCh0cmFpdCB+IGNociwgc2NhbGVzPSJmcmVlIikgKwogIHRoZW1lKHN0cmlwLnRleHQueSA9IGVsZW1lbnRfdGV4dChhbmdsZT0wKSwgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9OTApKSArCiAgZ2d0aXRsZSgiVU4gRWlnZW4gR2VuZSBRVEwiKQpwbC5VTgpnZ3NhdmUoIi4uL291dHB1dC9laWdlbiBnZW5lIGVRVEwgVU4gQ0lNIDIwMTIucGRmIix3aWR0aD0xMixoZWlnaHQ9OCkKYGBgCgoKIyMgTG9vayBmb3Igb3ZlcmxhcAoKRm9yIGVhY2ggZWlnZW4gZ2VuZSwgZmluZCBRVEwgYm9yZGVycyBhbmQgbG9vayBmb3Igb3ZlcmxhcCB3aXRoIGdyb3d0aCBRVEwKCkZvciBlYWNoIGVpZ2VuIGdlbmUgZmlyc3QgaWRlbnRpZnkgY2hyb21vc29tZXMgd2l0aCAic2lnbmlmaWNhbnQiIHBlYWtzIChpbiB0aGlzIGNhc2UgPiA5OSUgcGVybXVhdGlvbiB0aHJlc2hvbGQpIGFuZCB0aGVuIHJ1bnMgYmF5ZXNpbnQoKSBvbiB0aGVtIHRvIGRlZmluZSB0aGUgaW50ZXJ2YWxzCgpgYGB7cn0Kc2lnLmNocnMgPC0gc2Nhbm9uZS5nYXRoZXIgJT4lIGZpbHRlcihMT0QgPiBwZXJtLnRocmVzaG9sZCkgJT4lCiAgZ3JvdXBfYnkodHJhaXQsY2hyKSAlPiUKICBzdW1tYXJpemUodW5pcXVlKGNocikpCnNpZy5jaHJzCmBgYAoKbm93IGZvciBlYWNoIHNpZ25pZmljYW50IGNocm9tb3NvbWUvdHJhaXQgY29tYm8gcnVuIGJheWVzaW50CgpgYGB7cn0KI3JlbW92ZSBtYXJrZXJzIHdpdGhvdXQgcGh5c2ljYWwgcG9zaXRpb24Kc2Nhbm9uZV9laWdlbl9jaW0ucGh5cyA8LSBzY2Fub25lX2VpZ2VuX2NpbVshc3RyX2RldGVjdChyb3duYW1lcyhzY2Fub25lX2VpZ2VuKSwiXmNBIiksXQoKYmF5ZXNpbnQubGlzdCA8LSBhcHBseShzaWcuY2hycywxLGZ1bmN0aW9uKGhpdCkgewogICAgcmVzdWx0IDwtIGJheWVzaW50KHNjYW5vbmVfZWlnZW5fY2ltLnBoeXNbYygiY2hyIiwicG9zIixoaXRbInRyYWl0Il0pXSwgCiAgICAgICAgICAgICAgICAgICAgIGNocj1oaXRbImNociJdLCAKICAgICAgICAgICAgICAgICAgICAgbG9kY29sdW1uID0gMSwKICAgICAgICAgICAgICAgICAgICAgZXhwYW5kdG9tYXJrZXJzID0gVFJVRQogICkKICBjb2xuYW1lcyhyZXN1bHQpWzNdIDwtICJMT0QiCiAgcmVzdWx0Cn0pCgpuYW1lcyhiYXllc2ludC5saXN0KSA8LSBzaWcuY2hycyR0cmFpdAoKYmF5ZXNpbnQubGlzdCA8LSBsYXBwbHkoYmF5ZXNpbnQubGlzdCxmdW5jdGlvbih4KSB4ICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICAgcm93bmFtZXNfdG9fY29sdW1uKHZhcj0ibWFya2VybmFtZSIpICAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICBtdXRhdGUoY2hyPWFzLmNoYXJhY3RlcihjaHIpKQopCgpiYXllc2ludC5yZXN1bHQgPC0gYXMudGliYmxlKGJpbmRfcm93cyhiYXllc2ludC5saXN0LC5pZD0idHJhaXQiKSkgJT4lIAogIHNlbGVjdCh0cmFpdCxjaHIscG9zLG1hcmtlcm5hbWUsTE9EKSAlPiUKICBzZXBhcmF0ZShtYXJrZXJuYW1lLGludG89YygiY2hyMSIsIk1icCIpLHNlcD0ieCIsIGNvbnZlcnQ9VFJVRSkgJT4lCiAgZ3JvdXBfYnkodHJhaXQsY2hyKSAlPiUgCiAgc3VtbWFyaXplKHN0YXJ0PW1pbihNYnApLGVuZD1tYXgoTWJwKSxtaW5fZVFUTF9MT0Q9bWluKExPRCksbWF4X2VRVExfTE9EPW1heChMT0QpKSAlPiUgCiAgI2ZvciB0aGUgaGlnaCBRVEwgcGVha3MgdGhlIGludGVydmFsIHdpZHRoIGlzIDAuICBUaGF0IGlzIG92ZXJseSBwcmVjaXNlIGFuZCBuZWVkIHRvIHdpZGVuIHRob3NlLgogIG11dGF0ZShzdGFydD1pZmVsc2Uoc3RhcnQ9PWVuZCxtYXgoMCxzdGFydC0yMDAwMCksc3RhcnQpLCBlbmQ9aWZlbHNlKHN0YXJ0PT1lbmQsZW5kKzIwMDAwLGVuZCkpCiAgCiAgCmJheWVzaW50LnJlc3VsdApgYGAKCiMjIyBhbm5vdGF0ZSBFaWdlbiBnZW5lIFFUTAoKTG9hZCBhbm5vdGF0aW9uCmBgYHtyfQpCcmFwYUFubm90YXRpb24gPC0gcmVhZF9jc3YoIi4uL2lucHV0L0JyYXBhX1YxLjVfYW5ub3RhdGVkLmNzdiIpCkJyYXBhQW5ub3RhdGlvbgpgYGAKCmBgYHtyfQplaWdlbi5hbm5vdGF0ZWQgPC0gbGFwcGx5KDE6bnJvdyhiYXllc2ludC5yZXN1bHQpLGZ1bmN0aW9uKHJvdykgewogIHF0bCA8LSBiYXllc2ludC5yZXN1bHRbcm93LF0KICByZXN1bHRzIDwtIHN1YnNldChCcmFwYUFubm90YXRpb24sIGNocm9tPT1xdGwkY2hyICYKICAgICAgICAgICAgICAgICAgICBzdGFydCA+PSBxdGwkc3RhcnQgJgogICAgICAgICAgICAgICAgICAgIGVuZCA8PSBxdGwkZW5kKQp9CikKbmFtZXMoZWlnZW4uYW5ub3RhdGVkKSA8LSBiYXllc2ludC5yZXN1bHQkdHJhaXQKCmVpZ2VuLmFubm90YXRlZCA8LSBiaW5kX3Jvd3MoZWlnZW4uYW5ub3RhdGVkLC5pZD0idHJhaXQiKSAlPiUKICBtdXRhdGUoY2hyb209YXMuY2hhcmFjdGVyKGNocm9tKSkgJT4lCiAgbGVmdF9qb2luKGJheWVzaW50LnJlc3VsdCxieT1jKCJ0cmFpdCIsImNocm9tIj0iY2hyIikpICU+JSAjZ2V0IGVRVEwgTE9ECiAgcmVuYW1lKGVpZ2VuX2VRVExfY2FuZGlkYXRlPW5hbWUpCgplaWdlbi5hbm5vdGF0ZWQuc21hbGwgPC0gZWlnZW4uYW5ub3RhdGVkICU+JSBzZWxlY3QodHJhaXQsZWlnZW5fZVFUTF9jYW5kaWRhdGUsZW5kc193aXRoKCJMT0QiKSkKCmVpZ2VuLmFubm90YXRlZC5zbWFsbApgYGAKCmdpdmVuIGJheWVzaW50IHJlc3VsdHMsIGZpbmQgb3ZlcmxhcHMgd2l0aCBVTiBncm93dGggUVRMCgpgYGB7cn0KZmlsZXBhdGggPC0gIi4uL2lucHV0L0FsbDIwMTJIZWlnaHRRVEwyLnhsc3giCmZpbGViYXNlIDwtIGZpbGVwYXRoICU+JSBiYXNlbmFtZSgpICU+JSBzdHJfcmVwbGFjZSgiXFwuLiokIiwiIikKClFUTGdlbmVzIDwtIHJlYWR4bDo6cmVhZF9leGNlbChmaWxlcGF0aClbLC0xXQpRVExnZW5lcyA8LSBRVExnZW5lcyAlPiUgZHBseXI6OnJlbmFtZSguaWQ9UVRMLCBGVlR0cmFpdD1GVlQpICMgY2hhbmdlIG5hbWVzIHRvIG1hdGNoIHByZXZpb3VzIGZpbGUKUVRMZ2VuZXMgPC0gUVRMZ2VuZXMgJT4lIGZpbHRlcihzdHJfZGV0ZWN0KEZWVHRyYWl0LCJeVU4iKSkKUVRMZ2VuZXMKYGBgCgpgYGB7cn0KZWlnZW4ucXRsLmNvbWJpbmVkIDwtIGlubmVyX2pvaW4oZWlnZW4uYW5ub3RhdGVkLnNtYWxsLFFUTGdlbmVzLGJ5PWMoImVpZ2VuX2VRVExfY2FuZGlkYXRlIj0ibmFtZSIpKSAlPiUKICBzZWxlY3QoLmlkLCB0cmFpdCwgZXZlcnl0aGluZygpKQplaWdlbi5xdGwuY29tYmluZWQKYGBgCgpob3cgbWFueSBRVEwgaGF2ZSBhdCBsZWFzdCBzb21lIG92ZXJsYXA/CmBgYHtyfQp1bmlxdWUoUVRMZ2VuZXMkLmlkKQp1bmlxdWUoZWlnZW4ucXRsLmNvbWJpbmVkJC5pZCkKYGBgCgp0d28gb2YgZm91cgoKYXJlIGFsbCBlaWdlbiBnZW5lcyBvdmVybGFwcGluZz8KCmBgYHtyfQp1bmlxdWUoZWlnZW4uYW5ub3RhdGVkLnNtYWxsJHRyYWl0KQp1bmlxdWUoZWlnZW4ucXRsLmNvbWJpbmVkJHRyYWl0KQpgYGAKCk5vLCAyCgpgYGB7cn0Kd3JpdGVfY3N2KGVpZ2VuLnF0bC5jb21iaW5lZCwKICAgICAgICAgIHBhdGg9c3RyX2MoIi4uL291dHB1dC8iLCBmaWxlYmFzZSwgIl9laWdlblFUTF9vdmVybGFwX0NJTV8iLCBTeXMuRGF0ZSgpLCAiLmNzdiIpKQpgYGAKCiMjIG92ZXJsYXBzIGFuZCBzaWduZmljYW5jZQoKYGBge3J9CmVRVEwucmFuZ2VzIDwtIEdSYW5nZXMoYmF5ZXNpbnQucmVzdWx0JGNociwKICAgICAgICAgICAgICAgICAgICAgICByYW5nZXMgPSBJUmFuZ2VzKHN0YXJ0PWJheWVzaW50LnJlc3VsdCRzdGFydCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVuZD1iYXllc2ludC5yZXN1bHQkZW5kKSkKZVFUTC5yYW5nZXMgPC0gR2Vub21pY1Jhbmdlczo6cmVkdWNlKGVRVEwucmFuZ2VzKQpgYGAKCkRvIHRoZSBzaW11bGF0aW9ucwpgYGB7cn0Kc2ltcyA8LSAxMDAwCgpzZXQuc2VlZCg0NTQ1KQpzaW0ucmVzdWx0cyA8LSBzYXBwbHkoMTpzaW1zLCBmdW5jdGlvbihzKSB7CiAgaWYgKHMgJSUgMTAwID09IDApIHByaW50KHMpCiAgc2ltLmVRVEwgPC0gdGliYmxlKAogICAgY2hyPXNhbXBsZShjaHIuaW5mbyRjaHIsCiAgICAgICAgICAgICAgIHNpemUgPSBsZW5ndGgoZVFUTC5yYW5nZXMpLAogICAgICAgICAgICAgICByZXBsYWNlID0gVFJVRSwKICAgICAgICAgICAgICAgcHJvYj1jaHIuaW5mbyRlbmQvc3VtKGNoci5pbmZvJGVuZCkpLAogICAgd2lkdGg9d2lkdGgoZVFUTC5yYW5nZXMpICMgd2lkdGggb2YgdGhlIFFUTCB0byBzaW11bGF0ZQogICkKICBzaW0uZVFUTCA8LSBjaHIuaW5mbyAlPiUgCiAgICBzZWxlY3QoY2hyLGNoci5zdGFydD1zdGFydCxjaHIuZW5kPWVuZCkgJT4lIHJpZ2h0X2pvaW4oc2ltLmVRVEwsYnk9ImNociIpICNuZWVkIHRvIGdldCB0aGUgY2hyb20gZW5kIHNvIHdlIGNhbiBzYW1wbGUgY29ycmVjdGx5CiAgc2ltLmVRVEwgPC0gc2ltLmVRVEwgJT4lIG11dGF0ZShxdGwuc3RhcnQgPSBydW5pZihuPW4oKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbiA9IGNoci5zdGFydCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heD0gbWF4KGNoci5zdGFydCxjaHIuZW5kLXdpZHRoKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBxdGwuZW5kPXF0bC5zdGFydCt3aWR0aCkKICBzaW0uZVFUTC5yYW5nZXMgPC0gR1JhbmdlcyhzZXFuYW1lcyA9IHNpbS5lUVRMJGNociwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICByYW5nZXMgPSBJUmFuZ2VzKHN0YXJ0PXNpbS5lUVRMJHF0bC5zdGFydCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVuZD1zaW0uZVFUTCRxdGwuZW5kKSkKICAKICBzdXBwcmVzc1dhcm5pbmdzKHJlc3VsdCA8LSBzdW0oY291bnRPdmVybGFwcyhxdGwucmFuZ2VzLHNpbS5lUVRMLnJhbmdlcyk+MCkpCiAgcmVzdWx0Cn0pCgpgYGAKCgpgYGB7cn0KdHJ1ZS5vdmVybGFwIDwtIHN1bShjb3VudE92ZXJsYXBzKHF0bC5yYW5nZXMsZVFUTC5yYW5nZXMpKSAjT0sgdG8gaWdub3JlIHdhcm5pbmdzCgp0cnVlLm92ZXJsYXAKCm1lYW4oc2ltLnJlc3VsdHMgPj0gdHJ1ZS5vdmVybGFwKQoKdGliYmxlKEZWVFFUTF92c19NUmVRVExfVHJ1ZV9PdmVybGFwcz10cnVlLm92ZXJsYXAsCiAgICAgICBOX1NpbXVsYXRpb25zX2Zld2VyX292ZXJsYXBzPXN1bShzaW0ucmVzdWx0cyA8IHRydWUub3ZlcmxhcCksCiAgICAgICBOX1NpbXVsYXRpb25zX2dyZWF0ZXJfZXF1YWxfb3ZlcmxhcHM9c3VtKHNpbS5yZXN1bHRzID49IHRydWUub3ZlcmxhcCksCiAgICAgICBQX3ZhbHVlPW1lYW4oc2ltLnJlc3VsdHMgPj0gdHJ1ZS5vdmVybGFwKQopICU+JQogIHdyaXRlX2NzdihzdHJfYygiLi4vb3V0cHV0LyIsIGZpbGViYXNlLCAiX1dHQ05BX2VpZ2VuX2VRVExfQ0lNX292ZXJsYXBfcHZhbF8iLCBTeXMuRGF0ZSgpLCAiLmNzdiIpKQpgYGAKCgo=